<?php

defined('IN_MY_PHP') || die(0);

// 初始化本文件单独使用时涉及到的常量
defined('CRLF') || define('CRLF', "\r\n");
/**
 * A PDOORACLEDriver for ORACLE which convert PDOException to E_USER_ERROR
 * @author  netmou(leiyanfo@sina.com).
 * @since   2017/10/23.
 */
class PDOORACLEDriver extends PDO
{
	protected $host = ORACLE_DB_HOST;
	protected $dbName = ORACLE_DB_NAME;
	protected $port = ORACLE_DB_PORT;

	private $user = ORACLE_DB_USER;
	private $pwd = ORACLE_DB_PWD;

	//默认返回数据方式
	private static $fetchMode = parent::FETCH_ASSOC;
	//表结构缓存
	private $tablePRICache = array();
	private $tableColumnCache = array();
	private $assocKeyCounter = 0;
	private $lastQuerySQL = null;
	private $queryTplCheck = true;
	//请注意dsn中也能有字符集的设置
	private $options = array(
		parent::ATTR_PERSISTENT => false,
		parent::ATTR_ERRMODE => parent::ERRMODE_EXCEPTION,
	);

	//dsn-ref http://php.net/manual/en/ref.pdo-oci.connection.php

	public function __construct($config = array())
	{
		if (is_array($config) && !empty($config)) {
			$paramSet = array('host', 'dbName', 'port', 'user', 'pwd');
			foreach ($paramSet as $param) {
				if ($config[$param] !== null) {
					$this->{$param} = $config[$param];
				}
			}
		}
		$dsn = "oci:dbname={$this->host}:{$this->port}/{$this->dbName};charset=utf8";
		parent::__construct($dsn, $this->user, $this->pwd, $this->options);
	}

	/**获取只读的配置项：host*/
	public function getHost()
	{
		return $this->host;
	}

	/**获取只读的配置项：dbName*/
	public function getDbName()
	{
		return $this->dbName;
	}

	/**获取只读的配置项：port*/
	public function getPort()
	{
		return $this->port;
	}
	public function getLastQuerySQL()
	{
		return $this->lastQuerySQL;
	}

	public function formatKeyWord($keyword)
	{
		$keyword = str_replace('"', '', $keyword);
		return '"' . strtoupper(str_replace('.', '"."', $keyword)) . '"';
	}
	public function formatData($str, $escape = true)
	{
		if($escape){
			$str = str_replace('\'', '\'\'', $str);
		}
		return '\'' . $str . '\'';
	}
	public function filterData($columnVal, $columnInfo){
		if ($columnVal === '' && 0 == preg_match('/char|text|lob/i', $columnInfo['DATA_TYPE'])) {
			$columnVal = ($columnInfo['NULLABLE'] == 'Y')? null : $columnInfo['DATA_DEFAULT'];
		}
		return $columnVal;
	}

	/** 重写改变异常消息 */
	public function prepare($prepare, $option = array())
	{
		try {
			$this->lastQuerySQL = $prepare;
			return  parent::prepare($prepare, $option);
		} catch (PDOException $e) {
			trigger_error($e->getMessage() . CRLF . "--#with the query sql: ".$prepare, E_USER_ERROR);
		}
	}

	/** 重写来支持数据模版语法并改变异常消息 */
	public function query($query, $tplCheck = null, $fetchMode = null)
	{
		try {
			$tplCheck = ($tplCheck === null)? $this->queryTplCheck : $tplCheck;
			if ($tplCheck && preg_match_all('/\\{\\^((?:(?R)|(?!\\{\\^)(?!\\$\\}).+?)*)\\$\\}/s', $query, $matches)) {
				$valueData = array();
				for ($i=0; $i<count($matches[0]); $i++) {
					$valueData[] = $this->dec($matches[1][$i]);
					$pos = strpos($query, $matches[0][$i]);
					$query = substr_replace($query, '?', $pos, strlen($matches[0][$i]));
				}
				$this->lastQuerySQL = $query;
				$stmt = $this->prepare($query);
				$stmt->execute($valueData);
			} else {
				$this->lastQuerySQL = $query;
				$stmt = parent::query($query);
			}
			if ($fetchMode == null) {
				$fetchMode = self::$fetchMode;
			}
			$stmt->setFetchMode($fetchMode);

			return $stmt;
		} catch (PDOException $e) {
			trigger_error(CRLF . "--#".$e->getMessage() . CRLF . "--#with the query sql: ".$query, E_USER_ERROR);
		}
	}

	/** 重写改变异常消息 */
	public function exec($query, $bindSet = array(), $options = array())
	{
		try {
			$this->lastQuerySQL = $query;
			if ($bindSet === array()) {
				return parent::exec($query);
			} else {
				if(!is_array($bindSet)){
					$bindSet = array($bindSet);
				}
				$stmt = $this->prepare($query, $options);
				$stmt->setFetchMode(self::$fetchMode);
				$stmt->execute($bindSet); // php-manual:所有的值作为 PDO::PARAM_STR 对待

				return $stmt->rowCount();
			}
		} catch (PDOException $e) {
			trigger_error(CRLF . "--#".$e->getMessage() . CRLF . "--#with the query sql: ".$query, E_USER_ERROR);
		}
	}

	public function enc($data)
	{
		return '{^' . str_replace(array('\\', '{^', '$}'), array('\\\\', '\\{\\^', '\\$\\}'), $data). '$}';
	}

	private function dec(&$data)
	{
		return str_replace(array('\\{\\^', '\\$\\}', '\\\\'), array('{^', '$}', '\\'), $data);
	}

	public function setFetchMode($mode)
	{
		self::$fetchMode = $mode;
	}
	public function setQueryTplCheck($val)
	{
		$this->queryTplCheck = (bool)$val;
	}
	/**
	 * 取得一行结果集中某个字段值
	 *
	 * @param string $sql
	 * @param int    $fetchIndex
	 *
	 * @return mixed
	 */
	public function fetchField($sqlOrStmt, $fetchIndex = 0)
	{
		if (is_string($sqlOrStmt)) {
			$stmt = $this->query($sqlOrStmt);
		} else {
			$stmt = $sqlOrStmt;
		}

		$rtn = $stmt->fetchColumn($fetchIndex);
		return $rtn === false ? null : $rtn;
	}

	/**
	 * 取得单条记录.
	 *
	 * @param string $sql
	 * @param string $fetchMode
	 *
	 * @return array
	 */
	public function fetchRow($sqlOrStmt, $fetchMode = null)
	{
		if (is_string($sqlOrStmt)) {
			$stmt = $this->query($sqlOrStmt);
		} else {
			$stmt = $sqlOrStmt;
		}

		if ($fetchMode == null) {
			$fetchMode = self::$fetchMode;
		}
		$stmt->setFetchMode($fetchMode);
		$rtn = $stmt->fetch();
		return $rtn === false ? array() : $rtn;
	}

	/**
	 * 将结果集以键值对的形式储存到数组中.
	 *
	 * @param string $sql
	 *
	 * @return array
	 */
	public function fetchData($sqlOrStmt, $fetchMode = null)
	{
		if (is_string($sqlOrStmt)) {
			$stmt = $this->query($sqlOrStmt);
		} else {
			$stmt = $sqlOrStmt;
		}

		if ($fetchMode == null) {
			$fetchMode = self::$fetchMode;
		}
		$stmt->setFetchMode($fetchMode);
		$rtn = $stmt->fetchAll();
		return $rtn === false ? array() : $rtn;
	}

	/**
	 * 通过表名获取表结构(不缓存复合主键).
	 *
	 * @param string $tableName
	 */
	private function tableStruct($tableName)
	{

		//获取数据表的字段集合
		$real_tableName = $tableName;
		if (strpos($tableName, '.') !== false) {
			$real_tableName = substr(strrchr($tableName, '.'), 1);
		}
		$formatedTableName = $this->formatKeyWord($real_tableName);

		$columnSet = $this->fetchData("SELECT COLUMN_NAME, DATA_TYPE, DATA_DEFAULT, NULLABLE FROM USER_TAB_COLUMNS WHERE TABLE_NAME = '{$formatedTableName}'");
		foreach ($columnSet as $index => $column) {
			$upcase_colname = strtoupper($column['COLUMN_NAME']);
			$this->$tableColumnCache[$tableName][$upcase_colname] = $column;
		}

		//获取数据表的主键
		$pk_column_set = $this->fetchData("SELECT COL.COLUMN_NAME AS COLUMN_NAME FROM USER_CONSTRAINTS CON INNER JOIN USER_CONS_COLUMNS COL ON CON.CONSTRAINT_NAME = COL.CONSTRAINT_NAME WHERE CON.CONSTRAINT_TYPE = 'P' AND COL.TABLE_NAME = '{$formatedTableName}'");
		if (count($pk_column_set) == 1) {
			$this->tablePRICache[$tableName] = $pk_column_set[0]['COLUMN_NAME'];
		}
	}

	/**
	 * 过滤数据，剔除数据表中不包含的键值对,并缓存表结构.
	 * 本函数适用于向数据库更新数据.（对字段模版不做检查）.
	 *
	 * @param string $tableName 数据表名
	 * @param array  $data  键值对的集合
	 *
	 * @return array 能够更新数据库的键值对子集
	 */
	private function facadeData($tableName, &$data, $error = false)
	{
		if (!is_array($this->tableColumnCache[$tableName])) {
			$this->tableStruct($tableName);
		}
		$facadeSet = array();
		foreach ($data as $key => &$val) {
			if (is_scalar($key) && !is_numeric($key)) {
				$upcase_key = strtoupper($key);
				if ($columnInfo = $this->tableColumnCache[$tableName][$upcase_key]) {
					if (is_scalar($val) || is_null($val)) {
						$facadeSet[$key] = $this->filterData($val, $columnInfo);
					} elseif ($error == true) {
						trigger_error('值必须是标量！', E_USER_ERROR);
					}
				} elseif ($error == true) {
					trigger_error('不存在该字段！', E_USER_ERROR);
				}
			} elseif (is_numeric($key)) {
				$facadeSet[$key] = $val;
			} else {
				trigger_error('键必须是标量！', E_USER_ERROR);
			}
		}

		return $facadeSet;
	}

	/**
	 * 检查条件数据，不符合的予以异常处理,并缓存表结构.
	 * 本函数适用于 SQL 语句中 WHERE 条件部分.（对字段模版不做检查）.
	 *
	 * @param string $tableName 数据表名
	 * @param array  $data  键值对的集合
	 *
	 * @return array 能够插入数据库的键值对子集
	 */
	private function facadeCondition($tableName, &$data)
	{
		if (!is_array($this->tableColumnCache[$tableName])) {
			$this->tableStruct($tableName);
		}
		$facadeSet = array();
		foreach ($data as $key => &$val) {
			if (is_scalar($key) && !is_numeric($key)) {
				$upcase_key = strtoupper($key);
				$columnInfo = $this->tableColumnCache[$tableName][$upcase_key];
				if ($columnInfo !== null) {
					if (is_scalar($val) || is_null($val)) {
						$facadeSet[$key] = $val;
					} else {
						trigger_error('值必须是标量！', E_USER_ERROR);
					}
				} else {
					trigger_error("不存在该字段:{$val}！", E_USER_ERROR);
				}
			} elseif (is_numeric($key)) {
				// 简单的安全检测，防止书写错误
				if (!preg_match('/[\\(\\)><=\\s]+/s', trim($val))) {
					trigger_error('值好像不是有效的表达式！', E_USER_ERROR);
				}
				$facadeSet[$key] = $val;
			} else {
				trigger_error('条件的键值必须是合法标量！', E_USER_ERROR);
			}
		}

		return $facadeSet;
	}

	/**
	 * 通过表名获取主键.
	 *
	 * @param string $tableName
	 * @param bool   $error
	 *
	 * @return mixed
	 */
	public function mapTablePri($tableName, $error = false)
	{
		if (!array_key_exists($tableName, $this->tableColumnCache)) {
			$this->tableStruct($tableName);
		}
		$pri_key = $this->tablePRICache[$tableName];
		if ($error && $pri_key == null) {
			trigger_error('该表没有设主键（映射操作不支持复合主键）！');
		}

		return $pri_key;
	}

	/**
	 * 通过映射取得单条记录的某个字段.
	 *
	 * @param string $tableName
	 * @param mixed  $reflex    主键值或数组（键值对应字段，值对应字段值）
	 * @param string $field     字段名
	 *
	 * @return mixed
	 */
	public function mapField($tableName, $reflex, $field)
	{
		$conditionSet = array();
		if (!is_array($reflex)) {
			$pri_key = $this->mapTablePri($tableName, true);
			$conditionSet[$pri_key] = $reflex;
		} else {
			$conditionSet = &$reflex;
		}
		if (preg_match('/^\\{\\^(.+?)\\$\\}$/s', $field, $matches)) {
			$field = $this->dec($matches[1]);
		}else if ($field != null) {
			$field = $this->formatKeyWord($field);
		}else{
			trigger_error('MAP模式必须指定返回列！', E_USER_ERROR);
		}
		$conditionData = $this->buildPrepareCondition($conditionSet);
		$condition .= $conditionData[0];
		$formatedTableName = $this->formatKeyWord($tableName);
		$stmt = $this->prepare("SELECT {$field} FROM {$formatedTableName} WHERE {$condition} AND ROWNUM <= 1");
		$stmt->execute($conditionData[1]);


		return $this->fetchField($stmt);
	}

	/**
	 * 通过映射取得单条记录.
	 *
	 * @param string $tableName
	 * @param mixed  $reflex    主键值或数组（键值对应字段，值对应字段值）
	 * @param mixed  $fieldSet  返回那些字段
	 *
	 * @return mixed
	 */
	public function mapRow($tableName, $reflex, $fieldSet = '*')
	{
		$conditionSet = array();
		if (!is_array($reflex)) {
			$pri_key = $this->mapTablePri($tableName, true);
			$conditionSet[$pri_key] = $reflex;
		} else {
			$conditionSet = &$reflex;
		}
		$fieldSet = ($fieldSet != '*' && !is_array($fieldSet))? array($fieldSet) : $fieldSet;
		if (is_array($fieldSet)) {
			foreach ($fieldSet as $key => &$field) {
				if (preg_match('/^\\{\\^(.+?)\\$\\}$/s', $field, $matches)) {
					$fieldSet[$key] = $this->dec($matches[1]);
				} else {
					$fieldSet[$key] = $this->formatKeyWord($field);
				}
			}
			$fieldSet = implode(', ', $fieldSet);
		}
		$conditionData = $this->buildPrepareCondition($conditionSet);
		$condition .= $conditionData[0];
		$formatedTableName = $this->formatKeyWord($tableName);
		$stmt = $this->prepare("SELECT {$fieldSet} FROM {$formatedTableName} WHERE {$condition} AND ROWNUM <= 1");
		$stmt->execute($conditionData[1]);

		return $this->fetchRow($stmt);
	}

	/**
	 * 通过映射取得符合条件的所有数据.
	 *
	 * @param string $tableName
	 * @param mixed  $reflex    主键值或数组（键值对应字段，值对应字段值）
	 * @param string $field     字段名
	 *
	 * @return mixed
	 */
	public function mapData($tableName, $reflex = array(), $fieldSet = '*')
	{
		$conditionSet = array();
		if (!is_array($reflex)) {
			$pri_key = $this->mapTablePri($tableName, true);
			$conditionSet[$pri_key] = $reflex;
		} else {
			$conditionSet = &$reflex;
		}
		$fieldSet = ($fieldSet != '*' && !is_array($fieldSet))? array($fieldSet) : $fieldSet;
		if (is_array($fieldSet)) {
			foreach ($fieldSet as $key => &$field) {
				if (preg_match('/^\\{\\^(.+?)\\$\\}$/s', $field, $matches)) {
					$fieldSet[$key] = $this->dec($matches[1]);
				} else {
					$fieldSet[$key] = $this->formatKeyWord($field);
				}
			}
			$fieldSet = implode(', ', $fieldSet);
		}
		$formatedTableName = $this->formatKeyWord($tableName);
		if (empty($conditionSet)) {
			return $this->fetchData("SELECT {$fieldSet} FROM {$formatedTableName}");
		} else {
			$conditionData = $this->buildPrepareCondition($conditionSet);
			$condition .= $conditionData[0];
			$stmt = $this->prepare("SELECT {$fieldSet} FROM {$formatedTableName} WHERE {$condition}");
			$stmt->execute($conditionData[1]);

			return $this->fetchData($stmt);
		}
	}

	/**语法糖，求取指定条件的记录数量*/
	public function mapCount($tableName, $reflex = array())
	{

		return $this->mapField($tableName, $reflex, $this->enc('count(*)'));
	}


	/**
	* 仅获取列名的话可对返回结果【$ret】使用：
	* $simpleColumns=$func->arrayColumn($ret,'Field');
	*/
	public function mapColumns($tableName)
	{
		if (!is_array($this->tableColumnCache[$tableName])) {
			$this->tableStruct($tableName);
		}
		return $this->tableColumnCache[$tableName];
	}


	/**
	 * 通过映射主键表删除记录.
	 *
	 * @param string $tableName
	 * @param mixed  $reflex    主键值或数组（键值对应字段，值对应字段值）
	 *
	 * @return bool
	 */
	public function mapDelete($tableName, $reflex, $optimize = false)
	{
		$condition = array();
		if (!is_array($reflex)) {
			$pri_key = $this->mapTablePri($tableName, true);
			$condition[$pri_key] = $reflex;
		} else {
			$condition = &$reflex;
		}
		$condition = $this->facadeCondition($tableName, $condition);

		return $this->delete($tableName, $condition, $optimize);
	}

	/**
	 * 通过映射主键表更新记录.
	 *
	 * @param string $tableName
	 * @param mixed  $reflex    主键值或数组（键值对应字段，值对应字段值）
	 * @param array  $data      更新数据
	 *
	 * @return bool
	 */
	public function mapUpdate($tableName, $reflex, $data)
	{
		$condition = array();
		if (!is_array($reflex)) {
			$pri_key = $this->mapTablePri($tableName, true);
			$condition[$pri_key] = $reflex;
		} else {
			$condition = &$reflex;
		}
		$data = $this->facadeData($tableName, $data);
		$condition = $this->facadeCondition($tableName, $condition);

		return $this->update($tableName, $condition, $data);
	}

	/**
	 * 过滤并向数据库插入数据.
	 *
	 * @param string $tableName 数据表名
	 * @param array  $data  键值对的集合,通常来自表单
	 *
	 * @return bool
	 */
	public function postData($tableName, $data)
	{
		$insert = $this->facadeData($tableName, $data);

		return $this->insert($tableName, $insert);
	}

	/**
	 * 将数据插入到数据表.
	 *
	 * @param string $tableName 数据表名
	 * @param array  $data  键名对应字段名，键值对应字段值
	 *
	 * @return last_insert_id on success or 0 on failure
	 */
	private function insert($tableName, &$data)
	{
		if (empty($data)) {
			trigger_error('没有可插入的数据！', E_USER_ERROR);
		}
		$fieldStr = '';
		$valueStr = '';
		foreach ($data as $key => &$val) {
			$fieldStr .= ', '.$this->formatKeyWord($key);
			$valueStr .= ', :'.$key;
		}
		$fieldStr = substr($fieldStr, 1);
		$valueStr = substr($valueStr, 1);
		$formatedTableName = $this->formatKeyWord($tableName);
		$sql = 'INSERT INTO '.$formatedTableName.' ('.$fieldStr.') VALUES ('.$valueStr.')';
		$stmt = $this->prepare($sql);
		$this->bindAssocArrayValue($stmt, $data);
		$result = $stmt->execute();
		if(true == $result){
			return $this->lastInsertId();
		}
		return 0;
	}

	/**
	 * 更新数据表中的部分数据.(条件数据不支持 NULL操作).
	 *
	 * @param string $tableName     数据表名
	 * @param array  $data      键名对应字段名，键值对应字段值
	 * @param array  $condition 更新条件，不能为空
	 *
	 * @return bool
	 */
	private function update($tableName, &$condition, &$data)
	{
		if (empty($condition)) {
			trigger_error('没有指定条件，更新整个表很危险', E_USER_ERROR);
		}
		if (empty($data)) {
			trigger_error('没有可更新的数据！', E_USER_ERROR);
		}
		$formatedTableName = $this->formatKeyWord($tableName);
		$sql = 'UPDATE '.$formatedTableName.' SET ';
		$setData=array();
		foreach ($data as $key => &$val) {
			if (is_numeric($key)) {
				if (preg_match_all('/\\{\\^((?:(?R)|(?!\\{\\^)(?!\\$\\}).+?)*)\\$\\}/s', $val, $matches)) {
					for ($i=0; $i<count($matches[0]); $i++) {
						$sub_key = 'assoc_key_' . $this->assocKeyCounter++;
						$setData[$sub_key] = $this->dec($matches[1][$i]);
						$pos = strpos($val, $matches[0][$i]);
						$val = substr_replace($val, ':' . $sub_key, $pos, strlen($matches[0][$i]));
					}
				}
				$sql .= $val . ', ';
			} else {
				$setData[$key] = $val;
				$sql .= " {$this->formatKeyWord($key)} = :{$key}, ";
			}
		}
		//去掉尾部的‘, ’并连接 ‘where’
		$sql = substr($sql, 0, strlen($sql) - 2).' WHERE ';
		//防止两个数组有重复的键值，给条件键值加前缀
		$key_tag_condition = array();
		foreach ($condition as $key => &$val) {
			$key_tag_condition['CKP_'.$key] = ($val === null) ? '' : $val;
		}
		$conditionData=$this->buildPrepareAssocCondition($key_tag_condition, 'CKP_');
		$stmt = $this->prepare($sql . $conditionData[0]);
		$paramSet = array_merge($setData, $conditionData[1]);
		$this->bindAssocArrayValue($stmt, $paramSet);
		$stmt->execute();

		return $stmt->rowCount();
	}

	/**
	 * 删除数据表中的部分数据
	 * 删除大量数据时应执行optimize table tableName，来优化表结构.
	 *
	 * @param string $tableName     数据表名
	 * @param array  $condition 限定条件，不能为空
	 *
	 * @return bool
	 */
	private function delete($tableName, &$condition)
	{
		if (empty($condition)) {
			trigger_error('没有指定条件，清空表很危险', E_USER_ERROR);
		}
		$formatedTableName = $this->formatKeyWord($tableName);
		$sql = 'DELETE FROM '.$formatedTableName.' WHERE ';
		$conditionData = $this->buildPrepareCondition($condition);

		return $this->exec($sql . $conditionData[0], $conditionData[1]);
	}

	//绑定数据到预处理sql
	private function bindAssocArrayValue(&$stmt, &$paramSet)
	{
		if (is_object($stmt) && ($stmt instanceof PDOStatement)) {
			foreach ($paramSet as $key => &$value) {
				if (is_int($value)) {
					$param = PDO::PARAM_INT;
				} elseif (is_bool($value)) {
					$param = PDO::PARAM_BOOL;
				} elseif (is_null($value)) {
					$param = PDO::PARAM_NULL;
				} elseif (is_string($value) || is_float($value)) {
					$param = PDO::PARAM_STR;
				} else {
					trigger_error('Invalid data type!', E_USER_ERROR);
				}
				$stmt->bindValue(':'.$key, $value, $param);
			}
		}
	}

	/**
	 * 通过正则表达式替换sql中的返回字段
	 * 来提高获取SELECT查询记录总数的速度.
	 * 可以用fetchField方法查询自带count函数的sql
	 */
	 public function resultCount($query)
	{
		$regex = '/select((?:(?R)|(?!select|from).+?)*)from/si';
		// preg_match 匹配一次结束，出错返回 false
		if (preg_match($regex, $query, $output) === 1) {
			if(strpos($query, $output[0]) != 0){
				trigger_error('Invalid select sql!', E_USER_ERROR);
			}
			$pos = strpos($query, $output[1]);
			$query = substr_replace($query, ' count(*) ', $pos, strlen($output[1]));
			$stmt = $this->query($query);

			return (int) $stmt->fetchColumn();
		}

		trigger_error('Invalid select sql!', E_USER_ERROR);
	}

	/**
	 * 构造预查询条件.
	 *
	 * @param mixed $condition
	 *
	 * @return string
	 */
	public function buildPrepareCondition($condition, $error = false)
	{
		$conditionStr = '1=1';
		$valueData = array();
		foreach ($condition as $key => &$val) {
			if (is_scalar($val) || is_null($val)) {
				if (is_numeric($key)) {
					if (preg_match_all('/\\{\\^((?:(?R)|(?!\\{\\^)(?!\\$\\}).+?)*)\\$\\}/s', $val, $matches)) {
						for ($i=0; $i<count($matches[0]); $i++) {
							$valueData[] = $this->dec($matches[1][$i]);
							$pos = strpos($val, $matches[0][$i]);
							$val = substr_replace($val, '?', $pos, strlen($matches[0][$i]));
						}
					}
					$conditionStr = $conditionStr.' AND ('.$val.')';
				} else {
					$valueData[] = $val;
					$conditionStr = $conditionStr.' AND '.$this->formatKeyWord($key).' =?';
				}
			} elseif ($error) {
				trigger_error('条件值必须是合法标量！', E_USER_ERROR);
			}
		}

		return array($conditionStr, $valueData);
	}

	/**
	 * 构造键值对关联的预查询条件.
	 *
	 * @param mixed $condition
	 *
	 * @return string
	 */
	public function buildPrepareAssocCondition($condition, $prefix = '', $error = false)
	{
		$valueData=array();
		$conditionStr = '1=1';
		foreach ($condition as $key => &$val) {
			$field_key = substr($key, strlen($prefix));
			if (is_scalar($val) || is_null($val)) {
				if (is_numeric($field_key)) {
					if (preg_match_all('/\\{\\^((?:(?R)|(?!\\{\\^)(?!\\$\\}).+?)*)\\$\\}/s', $val, $matches)) {
						for ($i=0; $i<count($matches[0]); $i++) {
							$key = 'assoc_key_' . $this->assocKeyCounter++;
							$valueData[$key] = $this->dec($matches[1][$i]);
							$pos = strpos($val, $matches[0][$i]);
							$val = substr_replace($val, ':' . $key, $pos, strlen($matches[0][$i]));
						}
					}
					$conditionStr = $conditionStr.' AND ('.$val.')';
				} else {
					$valueData[$key] = $val;
					$conditionStr = $conditionStr.' AND '.$this->formatKeyWord($field_key).' = :'.$key;
				}
			} elseif ($error) {
				trigger_error('条件值必须是合法标量！', E_USER_ERROR);
			}
		}

		return array($conditionStr, $valueData);
	}
	/**
	 * 获取拼接的 INSERT 语句.
	 *
	 * @param string $tableName 表名
	 * @param array  $data  数据
	 * @param bool   $escape 是否转义
	 *
	 * @return string sql
	 */
	public function getInsertSql($tableName, $data, $escape = true)
	{
		$values = array();
		$fields = array();
		$data = $this->facadeData($tableName, $data);
		foreach ($data as $key => &$val) {
			if ($val !== null) {
				$values[] = $this->formatData($val, $escape);;
			} else {
				$values[] = 'NULL';
			}
			$fields[] = $this->formatKeyWord($key);
		}
		$tableName = $this->formatKeyWord($tableName);
		$sql = 'INSERT INTO '.$tableName.'('.implode(',', $fields).')VALUES('.implode(',', $values).')';

		return $sql;
	}
	/**
	 * 获取拼接的 UPDATE 语句.
	 *
	 * @param string $table 表名
	 * @param array  $data  数据
	 * @param mixed  $data  条件
	 * @param bool   $escape 是否转义
	 *
	 * @return string sql
	 */
	public function getUpdateSql($tableName, $data, $condition = null, $escape = true)
	{
		$sql = 'UPDATE '.$this->formatKeyWord($tableName).' SET ';
		$data = $this->facadeData($tableName, $data);
		foreach ($data as $key => &$val) {
			if ($val !== null) {
				$val = $this->formatData($val, $escape);
			} else {
				$val = 'NULL';
			}
			$sql .= $this->formatKeyWord($key).'='.$val.',';
		}
		$sql = substr($sql, 0, strlen($sql) - 1).' WHERE ';
		if ($condition != null && is_array($condition)) {
			$sql .= ' 1=1 ';
			$condition = $this->facadeCondition($tableName, $condition);
			foreach ($condition as $key => &$val) {
				$val = $this->formatData($val, $escape);
				$sql = $sql.' AND '.$this->formatKeyWord($key).'='.$val;
			}
		} elseif ($condition != null) {
			$condition = $this->formatData($condition, $escape);
			$sql = $sql.' '.$condition;
		}

		return $sql;
	}
}

# 初始化PDO并将可能出现的异常转换为错误输出

// try{
//     $pdo= new PDOORACLEDriver();
// }catch(PDOException $e){
//     trigger_error($e->getMessage(),E_USER_ERROR);
// }
//